#include "plms.h"

void PLMS()
    {
        Hint(WELCOME);
        Hint(SET_PARK_LOT_SIZE);
        int StackSize, QueueSize;
        scanf("%d %d", &StackSize, &QueueSize);

        Stack *ParkLotStack = InitStack(StackSize);
        Queue *CarQueue = InitQueue(QueueSize);

        bool IsStill = true;
        int item;

        while (IsStill)
            {
                OptMenu();
                scanf("%d", &item);
                system("cls");
                if (item == PARK_CAR)
                    ParkNewCar(ParkLotStack, CarQueue);
                else if (item == DRIVE_AWAY)
                    DriveAway(ParkLotStack, CarQueue);
                else if (item == VIEW_CAR)
                    ViewCar(ParkLotStack, CarQueue);
                else if (item == PRINT_TABLE)
                    PrintTable(ParkLotStack, CarQueue);
                else if (item == EXIT)
                    IsStill = false;
                else
                    Hint(INVALID_ITEM);
                item = 0;
          }
  }

// Inist System
Stack *InitStack(int StackSize)
    {
        Stack *ParkLotStack = (Stack*)malloc(sizeof(Stack));
        ParkLotStack->PtrArr = (Car**)malloc(StackSize * sizeof(Car**));
        ParkLotStack->top = -1;
        ParkLotStack->StackSize = StackSize;
        return ParkLotStack;
    }
Queue *InitQueue(int QueueSize)
    {
        Queue *CarQueue = (Queue*)malloc(sizeof(Queue));
        CarQueue->FirstNode = NULL;
        CarQueue->LastNode = NULL;
        CarQueue->len = 0;
        CarQueue->QueueSize = QueueSize;
        return CarQueue;
    }

// IsFull
bool IsStackFull(Stack *ParkLotStack)
    {
        return (ParkLotStack->top + 1 == ParkLotStack->StackSize);
    }
bool IsQueueFull(Queue *CarQueue)
    {
        return (CarQueue->len >= CarQueue->QueueSize);
    }

// Create New (Car*) Queue Node
//
Car *CreateCarNode()
    {
        return (Car*)malloc(sizeof(Car));
    }
// Register One Car
//
Car *RegisterOneCar()
    {
        char LicensePlateNumber[IDMAXSIZE];
        char ArriveTime[DATETIME];
        // do not forget string need a \0 on the end of them!
        // time format xx:xx
        Hint(REG_LICENSE_PLATE_NUMBER);
        scanf("%s", LicensePlateNumber);
        Hint(REG_ARRIVE_TIME);
        scanf("%s", ArriveTime);
        Car *NewCar = CreateCarNode();
        //could add valid check
        strcpy(NewCar->ID, LicensePlateNumber);
        strcpy(NewCar->ArriveTime, ArriveTime);

        return NewCar;
    }

// Search Car
void ViewCar(Stack *ParkLotStack, Queue *CarQueue)
    {
        Car *Target = SearchCar(ParkLotStack, CarQueue);
        if (Target)
            {
                if (Target->ParkPlace == PARKINGLOT)
                    printf("%s arrived at %s, in parking lot, on [%d]\n", Target->ID, Target->ArriveTime, ParkLotStack->top + 1);
                if (Target->ParkPlace == SIDEWALK)
                    printf("%s arrived at %s, in sidewalk, on [%d]\n", Target->ID, Target->ArriveTime, CarQueue->len);
            }
        else
            Hint(CAR_NOT_EXIST);
    }
Car *SearchCar(Stack *ParkLotStack, Queue *CarQueue)
    {
        char LicensePlateNumber[IDMAXSIZE];
        scanf("%s", LicensePlateNumber);
        Car *TargetCar = NULL;

        if (ParkLotStack->StackSize > 0)
            TargetCar = SearchInStack(ParkLotStack, LicensePlateNumber);
        if(!TargetCar && CarQueue->len > 0)
            TargetCar = SearchInQueue(CarQueue, LicensePlateNumber);
        return TargetCar;
    }
Car *SearchInStack(Stack *ParkLotStack, char *TargetCarID)
    {
        //use a double level pointer to traverse
        Car **PtrArr= ParkLotStack->PtrArr;

        int i = 0, cnt = 0, top = ParkLotStack->top;
        while (cnt < top + 1)
            {
                if (!strcmp(PtrArr[i]->ID, TargetCarID))
                    break;
                i++, cnt++;
            }

        if (cnt > top)
            return NULL;
        // return the pointer of target car
        return PtrArr[i];
    }
Car *SearchInQueue(Queue *CarQueue, char *TargetCarID)
    {
        int len = CarQueue->len;
        Car *p = CarQueue->FirstNode;

        int cnt = 0;
        while (cnt < len)
          {
              if (!strcmp(p->ID, TargetCarID))
                    break;
              cnt++;
              p = p->next;                
          }

        if (cnt == len)
            return NULL;
        return p;
    }

// Operate Car
void ParkNewCar(Stack *ParkLotStack, Queue *CarQueue)
    {
        if (!IsStackFull(ParkLotStack))
            PutCarInStack(ParkLotStack);
        else if (!IsQueueFull(CarQueue))
            AddCarInQueue(CarQueue);
        else
            Hint(NO_PLACE);
    }
void DriveAway(Stack *ParkLotStack, Queue *CarQueue)
    {
        Car *ThisCar = SearchCar(ParkLotStack, CarQueue);
        if (!ThisCar)
            {
                Hint(CAR_NOT_EXIST);
                return ;
            }
        if (ThisCar->ParkPlace == PARKINGLOT)
            RemoveFromStack(ParkLotStack, ThisCar);
        if (ThisCar->ParkPlace == SIDEWALK)
            RemoveFromQueue(CarQueue, ThisCar);

        scanf("%s", ThisCar->DriveAwayTime);
        CheckOut(ThisCar, 1);
        // If stack is not full and queue has car, push car in stack
        if (CarQueue->len != 0)
            if (!IsStackFull(ParkLotStack))
                {
                    PushCar(ParkLotStack, CarQueue->FirstNode);
                    CarQueue->FirstNode = CarQueue->FirstNode->next;
                    CarQueue->len--;
                }
    }

// Operate Car In Stack
void PutCarInStack(Stack *ParkLotStack)
    {
	    Hint(COULD_PARK_IN_STACK);
        printf("[%d] is avaliable\n", ParkLotStack->top + 1);
        PushCar(ParkLotStack, RegisterOneCar());
    }
void PushCar(Stack *ParkLotStack, Car *ThisCar)
    {
        ThisCar->ParkPlace = PARKINGLOT;
        ParkLotStack->top += 1;
        ThisCar->Serial = ParkLotStack->top;
        *(ParkLotStack->PtrArr + ParkLotStack->top) = ThisCar;
    }
void MoveCarInStack(Stack *ParkLotStack, int TargetCarSerial)
    {
        // only one car in the stack
		if (ParkLotStack->top == 0)
			{
				ParkLotStack->top = -1;
				return ;
			}
        int StackSize = ParkLotStack->StackSize;
        int top = ParkLotStack->top;

        // Init Temprary Stack 
        Stack *Temp = (Stack*)malloc(StackSize * sizeof(Stack));
        Temp = InitStack(StackSize);

        // move the car which after the target to temporary stack
        for (int temp_i = 0; top > TargetCarSerial; top--, temp_i++)
            {
                Temp->PtrArr[temp_i] = ParkLotStack->PtrArr[top];
                Temp->top++;
            }
        // move the car from temporary stack into the primary, begin at the place of target car
        for ( int i = TargetCarSerial, temp_i = Temp->top; temp_i >= 0; i++, temp_i--)
                ParkLotStack->PtrArr[i] = Temp->PtrArr[temp_i];
        ParkLotStack->top--;
    }
void RemoveFromStack(Stack *ParkLotStack, Car *TargetCar)
    {
        // use a temporary stack to move out the car, and let them in.
        MoveCarInStack(ParkLotStack, TargetCar->Serial);
        // above is the easyier way to out the TargetCar
        // int top = ParkLotStack->top;
        // if (top == 0)
        //     {
        //         ParkLotStack->top = -1;
        //         return ;
        //     }
        // int Serial = TargetCar->Serial;
        // for (int i = Serial; i < top; i++)
        //     ParkLotStack->PtrArr[i] = ParkLotStack->PtrArr[i + 1];
        // ParkLotStack->top = ParkLotStack->top - 1;
    }

// Operate Car In Queue
//
bool AddCarInQueue(Queue *CarQueue)
    {
	    Hint(COULD_PARK_IN_QUEUE);
        printf("[%d] is avaliable\n", CarQueue->len);
        AddCar(CarQueue, RegisterOneCar());
    }
bool AddCar(Queue *CarQueue, Car *NewCar)
    {
        NewCar->next = NULL;
        NewCar->ParkPlace = SIDEWALK;

        if (CarQueue->len == 0)
            {
                NewCar->Serial = 0;
                CarQueue->FirstNode = NewCar;
                CarQueue->LastNode = NewCar;
                CarQueue->len = 1;
                return true;
            }

        CarQueue->LastNode->next = NewCar;
        CarQueue->LastNode = CarQueue->LastNode->next;
        CarQueue->len++;
        NewCar->Serial = CarQueue->len;
    }
void DestoryQueue(Queue *CarQueue)
    {
        CarQueue->len = 0;
        CarQueue->FirstNode = NULL;
        CarQueue->LastNode = NULL; 
    }
void RemoveFromQueue(Queue *CarQueue, Car *Target)
    {
        int len = CarQueue->len;
        // p is a pointer used to traverse CarQueue
        Car *p = CarQueue->FirstNode;
        // DelCar is used to mark up the one which will be deleted, it will be free()
        Car *DeleteCar = NULL;
        // use count to make sure the traverse will stop before pointer over the linked list
        int cnt = 0;

        CarQueue->len--;

        if (len == 1) 
            {
                DestoryQueue(CarQueue);
                return ;
            }

        while (cnt < len)
            {
                // stop at the front (prior node) of the target car, it will be free()
                // the next of prior is the next of the target car
                if (cnt == 0 && p == Target)
                    break;
                if (p->next == Target)
                    break;
                p = p->next;
                cnt++;
            }    


        if (cnt == 0)
            {
                CarQueue->FirstNode = p->next;
                free(p);
                return ;
            }
        // mark up the delete car 
        DeleteCar = p->next;
        p->next = DeleteCar->next;
        free(DeleteCar);

        if (cnt == len)
            p->next = NULL;
    }
